/*!  \file  Queue.h
 *	  \brief  
 * 
 *   \details   details
 *   \author    Yordan Madzhunkov German Research School for Simulation Sciences GmbH
 *   \version   1.0
 *   \date      07/14/2013 05:08:46 PM
 *   \copyright GNU Public License.
 */
#pragma once
#include <iostream>
using namespace std;
template <typename T>
class NoLimitQueue {
public:
	NoLimitQueue(const int maxSize_); 
	virtual ~NoLimitQueue();						
	void push       (const T   id);
	bool tryPush    (const T   id);
	void pushNoLock (const T   id); // not thread safe
	bool pop        (      T & id);
	bool tryPop     (      T & id);
	void reset();
	bool isEmpty () const { return start == stop; };
	bool notEmpty() const { return start != stop; };
	int  size();
	friend class MemoryPool;

protected:
	DISALLOW_COPY_AND_ASSIGN(NoLimitQueue);
	inline void push_   (const T   id);
	inline void pop_    (      T & id);
	inline virtual void reset_();

	T      * RS storage;
	Lock startLock;
	Lock  stopLock;
	int maxSize, maxThreads, start, stop;
	
};
template <typename T>
class Queue : public NoLimitQueue<T> {
public:
	Queue(const int maxSize_, const int maxThreads_); 
	virtual ~Queue();						
	// inserts a id into the queue, returns false if queue is full
	// if an other thread is trying to insert something else, this method will
	// wait and then insert the element
	bool push   (const T   id, const int tID);
	// same as push, but can also return false if an other thread is pushing 
	// in the same time. tryPush promise quick retern.
	bool tryPush(const T   id, const int tID);

	// pushes in queue without locking/unlocking
	bool pushNoLock (const T   id, const int tID);

	// pop removes one element from the queue
	// returns false if queue is empty
	bool pop    (      T & id, const int tID);
	// same as pop
	// quick return
	// false if an other thread is already poping from queue
	bool tryPop (      T & id, const int tID);
	// thread safe, simply reset everything :) 	
//	void reset();
	friend class MemoryPool;
private:
//	DISALLOW_COPY_AND_ASSIGN(Queue);
	inline virtual void reset_();

	int    * RS count;
	int maxThreads, limit;

};

template <typename T>
NoLimitQueue<T>::NoLimitQueue(const int maxSize_){ 
	maxSize    = maxSize_;
	storage    = new T [maxSize   ];
	reset_();
}
template <typename T>
NoLimitQueue<T>::~NoLimitQueue(){
	delArr(storage);
};					
template <typename T>
void NoLimitQueue<T>::push   (const T   id) {
	stopLock.lock(); 	push_(id);	stopLock.unlock(); 
}

template <typename T>
void NoLimitQueue<T>::push_   (const T   id) {
	storage[stop++] = id;
	stop = stop < maxSize ? stop : 0;
}
template <typename T>
void NoLimitQueue<T>::pop_   ( T &  id) {
	id = storage[start++];
	start = start < maxSize ? start : 0;
}

template <typename T>
void NoLimitQueue<T>::pushNoLock (const T   id) {
	push_(id);
}
template <typename T>
bool NoLimitQueue<T>::tryPush(const T   id) {
	if (stopLock.tryLock()) {
		push_(id);
		stopLock.unlock(); 
		return true;
	}
		return false;
}

template <typename T>
bool NoLimitQueue<T>::pop    (      T & id) {
	if (notEmpty()) {
		startLock.lock(); 
		if (notEmpty()) {
			pop_(id);
			startLock.unlock(); 
			return true;
		} 
		startLock.unlock(); 
	}
	return false;
}
template <typename T>
bool NoLimitQueue<T>::tryPop    (      T & id) {
	if (notEmpty()) {
		if ( startLock.tryLock()) {
			if (start != stop) {
				pop_(id);
				startLock.unlock(); 
				return true;
			} 
			startLock.unlock(); 
		}
	}
	return false;
}
template <typename T>
void NoLimitQueue<T>::reset() {
	startLock.lock();	stopLock.lock();
	reset_();
	startLock.unlock();	stopLock.unlock();
}
template <typename T>
inline void NoLimitQueue<T>::reset_() {
	start      = 0;
	stop       = 0;
}

template <typename T>
inline int NoLimitQueue<T>::size() {
	startLock.lock();	stopLock.lock();
	int res = stop >= start ? stop - start : maxSize + stop - start;
	startLock.unlock();	stopLock.unlock();
	return res;
};



template <typename T>
Queue<T>::Queue(const int maxSize_, const int maxThreads_) : NoLimitQueue<T>(maxSize_){ // not thread save
	maxThreads = maxThreads_;
	limit      = this->maxSize / maxThreads;
	count      = new  int [maxThreads];
	reset_();
}
template <typename T>
Queue<T>::~Queue(){	
	delArr(count  );
};					


template <typename T>
bool Queue<T>::push   (const T   id, const int tID) {
	if (count[tID] < limit) {
		NoLimitQueue<T>::push(id);
		count[tID]++;
		return true;
	}
	return false;
}

template <typename T>
bool Queue<T>::pushNoLock (const T   id, const int tID) {
	if (count[tID] < limit) {
		NoLimitQueue<T>::push(id);
		count[tID]++;
		return true;
	}
	return false;
}
template <typename T>
bool Queue<T>::tryPush(const T   id, const int tID) {
	if (count[tID] < limit) 
		if (this->stopLock.tryLock()) {
			push_(id);
			this->stopLock.unlock(); 
			count[tID]++;
			return true;
		}
	return false;
}



template <typename T>
bool Queue<T>::pop    (      T & id, const int tID) {
	this->startLock.lock(); 
	if (this->notEmpty()) {
		pop_(id);
		count[tID]--;
		this->startLock.unlock(); 
		return true;
	} else {
		this->startLock.unlock(); 
		return false;
	}
}
template <typename T>
bool Queue<T>::tryPop (      T & id, const int tID) {
	if (this->startLock.tryLock()) {
		if (this->notEmpty()) {
			pop_(id);
			count[tID]--;
			this->startLock.unlock(); 
			return true;
		} else {
			this->startLock.unlock(); 
			return false;
		}
	}
	return false;
}
template <typename T>
inline void Queue<T>::reset_() {
	NoLimitQueue<T>::reset_();
	memset(count, 0, sizeof(count[0]) * maxThreads);
}
